package servidor;

import elementosJuego.*;
import estado.EstadoJuego;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Responsabilidad: Contiene la lógica del juego, encargado de detectar
 * colisiones y ordenar movimientos y creacion de balas.
 * Mensajes posibles: CrearPartida, AgregarJugador, FinPartida, Evento (movimiento o bala).
 */

public class GestorJuego implements Runnable{
    
    private List balas;
    private Map jugadores;
    private ArrayList<Pared> paredes;
    private Iterator it;
    private GestorPartida gestorPartida;
    private Thread hilo;
    private EstadoJuego estadoJuego;

    public GestorJuego(int numJugadores, int tipoEscenario, GestorSalida gestorSalida, GestorPartida gestorPartida) {
        this.gestorPartida=gestorPartida;
        balas = Collections.synchronizedList(new ArrayList<Bala>());
        jugadores = Collections.synchronizedMap(new HashMap(numJugadores));

        generarEscenario();
    
        hilo = new Thread(this);
        
        for(int i=0;i<numJugadores;i++){
            if(i==0){
                jugadores.put(i, new Jugador(15,120,this, i));
                ((Jugador)jugadores.get(i)).setApunta('B');
            }else if(i==1){
                jugadores.put(i, new Jugador(715,595,this, i));
                ((Jugador)jugadores.get(i)).setApunta('A');
            }else if(i==2){
                jugadores.put(i, new Jugador(715,120,this, i));
                ((Jugador)jugadores.get(i)).setApunta('B');
            }else{
                jugadores.put(i, new Jugador(15,595,this, i));
                ((Jugador)jugadores.get(i)).setApunta('B');
            }
        }
        estadoJuego = new EstadoJuego(new GestorActualizacion(this, gestorSalida));
        hilo.start();
    }

    private void generarEscenario() {
        paredes = new ArrayList<Pared>();
                    /**BORDES*/
            paredes.add(new Pared(0, 100, 760, 3, this));
            paredes.add(new Pared(0, 100, 3, 540, this));
            paredes.add(new Pared(757, 100, 3, 540, this));
            paredes.add(new Pared(0, 637, 760, 3, this));

            /**PAREDES IZQUIERDA */

            paredes.add(new Pared(60,100,10,100,this));
            paredes.add(new Pared(60,260,10,200,this));
            paredes.add(new Pared(60,540,10,100,this));

            paredes.add(new Pared(130,160,10,185,this));
            paredes.add(new Pared(130,405,10,185,this));

            /**PAREDES CENTRO*/
            paredes.add(new Pared(200,160,10,185,this));
            paredes.add(new Pared(200,405,10,185,this));

            paredes.add(new Pared(200,160,150,10,this));
            paredes.add(new Pared(420,160,150,10,this));

            paredes.add(new Pared(200,580,150,10,this));
            paredes.add(new Pared(420,580,150,10,this));

            paredes.add(new Pared(560,160,10,185,this));
            paredes.add(new Pared(560,405,10,185,this));

            /**PAREDES DERECHA */
            paredes.add(new Pared(700,100,10,100,this));
            paredes.add(new Pared(700,260,10,200,this));
            paredes.add(new Pared(700,540,10,100,this));

            paredes.add(new Pared(630,160,10,185,this));
            paredes.add(new Pared(630,405,10,185,this));
    }

    public GestorPartida getGestorPartida() {
        return gestorPartida;
    }

    public synchronized void mover(int id, char accion){
        try {
            jugadores.get(id).getClass().getMethod("mover" + accion, (Class[]) null).invoke(jugadores.get(id), (Object[]) null);
            estadoJuego.Actualizar();
        } catch (IllegalAccessException ex) {
            Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvocationTargetException ex) {
            ex.getCause();
            Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void crearBala(int id){
        balas.add(new Bala(((Jugador)jugadores.get(id)).getPosicionBala()[0], ((Jugador)jugadores.get(id)).getPosicionBala()[1], this, ((Jugador)jugadores.get(id)).getApunta()));
    }
    public synchronized void detectarColision(Figura figura){
        for(int i=0;i<balas.size();i++){
            try {
                figura.getClass().getMethod("colisionoBala" + Boolean.valueOf(figura.rect().intersects(((Bala)balas.get(i)).rect())).toString(), Integer.class).invoke(figura, i);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchMethodException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SecurityException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        it = jugadores.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry e = (Map.Entry)it.next();
            try {
                figura.getClass().getMethod("colisionoJugador" + Boolean.valueOf(figura.rect().intersects(((Jugador)e.getValue()).rect())).toString(), Integer.class).invoke(figura, ((Jugador)e.getValue()).getId());
            } catch (IllegalAccessException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchMethodException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SecurityException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        for(int i=0;i<paredes.size();i++){
            try {
                figura.getClass().getMethod("colisionoPared" + Boolean.valueOf(figura.rect().intersects(paredes.get(i).rect())).toString(), Integer.class).invoke(figura, i);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchMethodException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SecurityException ex) {
                Logger.getLogger(GestorJuego.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public List getBalas(){
        return balas;
    }
    public Map getJugadores(){
        return jugadores;
    }
    public ArrayList getParedes(){
        return paredes;
    }

    public void eliminarJugador(int id){
        synchronized(jugadores){
            jugadores.remove(id);
        }
        it = jugadores.entrySet().iterator();
        Map.Entry e = (Map.Entry)it.next();
        gestorPartida.eliminarJugador(id);
        if(jugadores.size() == 1){
            gestorPartida.finalizarPartida(((Jugador)e.getValue()).getId());
        }
    }

    public EstadoJuego getEstadoJuego(){
        return estadoJuego;
    }

    public void run() {
        Thread miHilo = Thread.currentThread();
        while(miHilo == hilo) {
            try{
                Thread.sleep(10);
            }catch(Exception e){}

            for (int i=0;i<balas.size();i++)
                ((Bala)balas.get(i)).movete();
            if( !balas.isEmpty() )
                estadoJuego.Actualizar();
        }
    }
}